package cn.ex.de001.common.utils.tokenUtils;

import cn.ex.de001.exception.BaseException;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 包括了token的签发，验证以及根据token获取账号
 */
public class Token {
    private static final Logger logger = LoggerFactory.getLogger(Token.class);
    /**
     * 过期30分钟
     * */
    private static final long EXPIRE_TIME = 30 * 60 * 1000;

    /**
     * jwt密钥
     * */
    private static final String SECRET = "jwt_secret";

    //存放token的map集合
    @Getter
    public static ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();

    public static void setMap(ConcurrentHashMap<String, String> map) {
        Token.map = map;
    }

    /**
     * 生成jwt字符串，30分钟后过期  JWT(json web token)
     * @param account
     * @return
     * */
    public static String sign(String account) {//签发token
        try {
            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            return JWT.create()
                    //将account保存到token里面
                    .withAudience(account)
                    //五分钟后token过期
                    .withExpiresAt(date)
                    //token的密钥
                    .sign(algorithm);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据token获取account
     * @param token
     * @return
     * */
    public static String getAccount(String token) {
        try {
            String account = JWT.decode(token).getAudience().get(0);
            return account;
        }catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 校验token
     * @param token
     * @return
     * */
    public static boolean checkSign(String token) {
        try {
            Algorithm algorithm  = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                    //.withClaim("username, username)
                    .build();
            verifier.verify(token);
            return true;
        } catch (JWTVerificationException e) {
            logger.warn("token无效");
            String account=Token.getAccount(token);//将该token从白名单内移除
            if(account!=null){
                Token.map.remove(account);//移出白名单
            }
            return false;//token无效
        }
    }

    /**
     * 过期时间
     * @param token
     */
    public static Long expiration(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                    .build(); // 创建验证器
            DecodedJWT jwt = verifier.verify(token); // 验证并解码 JWT
            Date expirationDate = jwt.getExpiresAt();
            logger.info("令牌到期时间: " + expirationDate);
            long nowMillis = System.currentTimeMillis();
            long time = -1;
            if (expirationDate != null) {
                long expMillis = expirationDate.getTime();
                long diff = expMillis - nowMillis;
                if (diff > 0) {
                    time = diff/1000;
                } else {
                    throw new BaseException("令牌已经过期.");
                }
            } else {
                throw new BaseException("令牌中找不到过期时间.");
            }
            return time;
        } catch (JWTVerificationException e) {
            throw new BaseException("验证和解析令牌失败.");
        }
    }

    public static void main(String[] args) {
        String sign = sign("123456789");
        System.out.println("sign数据: "+ sign);
        boolean stream = checkSign(sign);
        System.out.println("stream: "+ stream);
        String account = getAccount(sign);
        System.out.println("account: "+ account);
    }
}
